﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Pap.Core.Utilities;
using Pap.Data;
using Pap.Services.Customer;
using Pap.Web.Utils;

namespace Pap.Web.Modules.Loan.ViewModel
{
    /// <summary>
    /// 
    /// </summary>
    public class CreateCustomerViewModel : IViewModel
    {
        /// <summary>
        /// 
        /// </summary>
        ILoanRepository loanRepository;
        /// <summary>
        /// Initializes a new instance of the <see cref="CreateCustomerViewModel"/> class.
        /// </summary>
        /// <param name="loanRepository">The loan repository.</param>
        public CreateCustomerViewModel(ILoanRepository loanRepository)
        {
            this.loanRepository = loanRepository;
        }

        /// <summary>
        /// Searches the customers.
        /// </summary>
        /// <param name="SSN">The SSN.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public HybridCustomerDisplay[] SearchCustomers(string SSN, string name)
        {
            SSN = SSN.ToLower();
            name = name.ToLower();
            List<HybridCustomerDisplay> result = new List<HybridCustomerDisplay>();
            #region from Customer Table
            if (!SSN.IsNullOrEmpty())
            {
                var fromCustTable = loanRepository.FindCustomers(c => c.SSN.Contains(SSN));
                foreach (var cust in fromCustTable)
                {
                    HybridCustomerDisplay displ = new HybridCustomerDisplay()
                    {
                        ID = cust.CustomerID,
                        Name = cust.CustomerName,
                        SSN = cust.SSN
                    };
                    result.Add(displ);
                }
            }

            if (!name.IsNullOrEmpty())
            {
                IEnumerable<ICustomer> customers = loanRepository.FindCustomers(c => c.CustomerName.ToLower().Contains(name));
                if (customers.Count() > 0)
                {
                    foreach (var cust in customers)
                    {
                        HybridCustomerDisplay displ = new HybridCustomerDisplay()
                        {
                            ID = cust.CustomerID,
                            Name = cust.CustomerName,
                            SSN = cust.SSN
                        };
                        result.Add(displ);
                    }
                }
            }
            #endregion

            #region From Loan History Table
            if (!SSN.IsNullOrEmpty())
            {
                var fromLoanHistory = loanRepository.FindCustomerHistories(h => h.OldCustomerCode.Contains(SSN)).Distinct();
                foreach (var hist in fromLoanHistory)
                {
                    HybridCustomerDisplay displ = new HybridCustomerDisplay();
                    if (hist.LoanID != null)
                    {
                        ICustomer cust = loanRepository.GetCustomerLoanByID((Guid)hist.LoanID).CustomerObj;
                        displ.ID = cust.CustomerID;
                        displ.Name = cust.CustomerName;
                        displ.SSN = cust.SSN;
                    }
                    else
                        displ.Name = hist.CustomerName;

                    result.Add(displ);
                }
            }

            if (!name.IsNullOrEmpty())
            {
                name = name.ToLower();
                var fromLoanHistory = loanRepository.FindCustomerHistories(h => h.CustomerName.ToLower().Contains(name)).Distinct();
                foreach (var hist in fromLoanHistory)
                {
                    HybridCustomerDisplay displ = new HybridCustomerDisplay();
                    if (hist.LoanID != null)
                    {
                        ICustomer cust = loanRepository.GetCustomerLoanByID((Guid)hist.LoanID).CustomerObj;
                        displ.ID = cust.CustomerID;
                        displ.Name = cust.CustomerName;
                        displ.SSN = cust.SSN;
                    }
                    else
                        displ.Name = hist.CustomerName;

                    result.Add(displ);
                }
            }
            #endregion

            return result.ToArray();
        }

        /// <summary>
        /// Validates the customer.
        /// </summary>
        /// <param name="customerID">The customer ID.</param>
        /// <param name="customerName">Name of the customer.</param>
        /// <returns></returns>
        public List<ValidationError> ValidateCustomer(string customerID, string customerName)
        {
            List<CustomerValidatorBase> validatorsList = ServiceLocator.Current.GetAllInstances<CustomerValidatorBase>().ToList();

            List<ValidationError> errList = null;
            if (!customerID.IsNullOrEmpty() && customerID.ToGuid() != Guid.Empty)
            {
                ICustomer c = loanRepository.GetCustomerByID(customerID.ToGuid());
                errList = ValidateByCustomerObj(c, validatorsList);
            }
            else
            {
                errList = ValidateByCustomerName(customerName, validatorsList);
            }
            return errList;
        }

        /// <summary>
        /// Validates the name of the by customer.
        /// </summary>
        /// <param name="customerName">Name of the customer.</param>
        /// <param name="validatorsList">The validators list.</param>
        /// <returns></returns>
        private static List<ValidationError> ValidateByCustomerName(string customerName, List<CustomerValidatorBase> validatorsList)
        {
            List<ValidationError> errList = new List<ValidationError>();
            foreach (CustomerValidatorBase val in validatorsList)
            {
                if (val.CanValidateByName)
                {
                    if (!val.Validate(RegistryInfo.CommitteeID, customerName))
                        errList.Add(new ValidationError() { IsError = val.IsError, StatusString = val.InvalidMessage });
                }
            }
            return errList;
        }

        /// <summary>
        /// Validates the by customer obj.
        /// </summary>
        /// <param name="c">The c.</param>
        /// <param name="validatorsList">The validators list.</param>
        /// <returns></returns>
        private static List<ValidationError> ValidateByCustomerObj(ICustomer c, List<CustomerValidatorBase> validatorsList)
        {
            List<ValidationError> errList = new List<ValidationError>();
            foreach (CustomerValidatorBase val in validatorsList)
            {
                if (!val.Validate(RegistryInfo.CommitteeID, c))
                    errList.Add(new ValidationError() { IsError = val.IsError, StatusString = val.InvalidMessage });
            }
            return errList;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class HybridCustomerDisplay
    {
        /// <summary>
        /// Gets or sets the ID.
        /// </summary>
        /// <value>
        /// The ID.
        /// </value>
        public Guid ID { get; set; }
        /// <summary>
        /// Gets or sets the SSN.
        /// </summary>
        /// <value>
        /// The SSN.
        /// </value>
        public string SSN { get; set; }
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        public string Name { get; set; }
    }

    /// <summary>
    /// 
    /// </summary>
    public class ValidationError : IComparable<ValidationError>
    {
        /// <summary>
        /// Gets or sets a value indicating whether this instance is error.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is error; otherwise, <c>false</c>.
        /// </value>
        public bool IsError { get; set; }
        /// <summary>
        /// Gets or sets the status string.
        /// </summary>
        /// <value>
        /// The status string.
        /// </value>
        public string StatusString { get; set; }
        #region IComparable<ValidationError> Members

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings:
        /// Value
        /// Meaning
        /// Less than zero
        /// This object is less than the <paramref name="other"/> parameter.
        /// Zero
        /// This object is equal to <paramref name="other"/>.
        /// Greater than zero
        /// This object is greater than <paramref name="other"/>.
        /// </returns>
        int IComparable<ValidationError>.CompareTo(ValidationError other)
        {
            int errorCompare = this.IsError.CompareTo(other.IsError);
            if (errorCompare == 0)
            {
                return this.StatusString.Length.CompareTo(other.StatusString.Length);
            }
            else
            {
                return 0 - errorCompare;
            }
        }

        #endregion
    }
}
